home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / smtp.c < prev    next >
C/C++ Source or Header  |  1996-06-28  |  12KB  |  351 lines

  1. /*
  2.  * Program:    Simple Mail Transfer Protocol (SMTP) routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    27 July 1988
  13.  * Last Edited:    28 June 1996
  14.  *
  15.  * Sponsorship:    The original version of this work was developed in the
  16.  *        Symbolic Systems Resources Group of the Knowledge Systems
  17.  *        Laboratory at Stanford University in 1987-88, and was funded
  18.  *        by the Biomedical Research Technology Program of the National
  19.  *        Institutes of Health under grant number RR-00785.
  20.  *
  21.  * Original version Copyright 1988 by The Leland Stanford Junior University
  22.  * Copyright 1994 by the University of Washington
  23.  *
  24.  *  Permission to use, copy, modify, and distribute this software and its
  25.  * documentation for any purpose and without fee is hereby granted, provided
  26.  * that the above copyright notices appear in all copies and that both the
  27.  * above copyright notices and this permission notice appear in supporting
  28.  * documentation, and that the name of the University of Washington or The
  29.  * Leland Stanford Junior University not be used in advertising or publicity
  30.  * pertaining to distribution of the software without specific, written prior
  31.  * permission.  This software is made available "as is", and
  32.  * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
  33.  * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
  34.  * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  35.  * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
  36.  * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
  37.  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  38.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  39.  * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
  40.  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  41.  *
  42.  */
  43.  
  44. #include <ctype.h>
  45. #include <stdio.h>
  46. #include "mail.h"
  47. #include "osdep.h"
  48. #include "smtp.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51.  
  52.  
  53. /* Mailer parameters */
  54.  
  55. long smtp_port = 0;        /* default port override */
  56.  
  57. /* Mail Transfer Protocol open connection
  58.  * Accepts: service host list
  59.  *        SMTP open options
  60.  * Returns: T on success, NIL on failure
  61.  */
  62.  
  63. SMTPSTREAM *smtp_open (char **hostlist,long options)
  64. {
  65.   SMTPSTREAM *stream = NIL;
  66.   char *s,tmp[MAILTMPLEN];
  67.   void *tcpstream;
  68.   if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR);
  69.   else do {            /* try to open connection */
  70.     if (smtp_port) sprintf (s = tmp,"%s:%ld",*hostlist,smtp_port);
  71.     else s = *hostlist;        /* get server name */
  72.     if (tcpstream = tcp_open (s,"smtp",SMTPTCPPORT)) {
  73.       stream = (SMTPSTREAM *) fs_get (sizeof (SMTPSTREAM));
  74.       stream->tcpstream = tcpstream;
  75.       if(smtp_greeting (stream,
  76.              strcmp ("localhost",(char*)lcase(strcpy(tmp,*hostlist)))
  77.                   ? tcp_localhost (tcpstream) : "localhost",
  78.              options))
  79.     return stream;
  80.       smtp_close (stream);    /* otherwise punt stream */
  81.     }
  82.   } while (*++hostlist);    /* try next server */
  83.   return NIL;
  84. }
  85.  
  86.  
  87. /* Mail Transfer Protocol salutation
  88.  * Accepts: local host name
  89.  *        SMTP open options
  90.  * Returns: T on success, NIL on failure
  91.  */
  92. long smtp_greeting (SMTPSTREAM *stream,char *lhost,long options)
  93. {
  94.   stream->size = 0;        /* size limit */
  95.   stream->debug = (options & SOP_DEBUG) ? T : NIL;
  96.   stream->esmtp = (options & SOP_ESMTP) ? T : NIL;
  97.   stream->ok_send = stream->ok_soml = stream->ok_saml = stream->ok_expn =
  98.     stream->ok_help = stream->ok_turn = stream->ok_size =
  99.       stream->ok_8bitmime = NIL;
  100.   stream->reply = NIL;
  101.                 /* get SMTP greeting */
  102.   if (smtp_reply (stream) == SMTPGREET) {
  103.     if ((stream->ehlo = stream->esmtp) &&
  104.     (smtp_send (stream,"EHLO",lhost) == SMTPOK)) return T;
  105.                 /* try ordinary SMTP then */
  106.     stream->ehlo = stream->esmtp = NIL;
  107.     if (smtp_send (stream,"HELO",lhost) == SMTPOK) return T;
  108.   }
  109.   mm_log (stream->reply,ERROR);
  110.   return NIL;
  111. }
  112. /* Mail Transfer Protocol close connection
  113.  * Accepts: stream
  114.  * Returns: NIL always
  115.  */
  116.  
  117. SMTPSTREAM *smtp_close (SMTPSTREAM *stream)
  118. {
  119.   if (stream) {            /* send "QUIT" */
  120.     smtp_send (stream,"QUIT",NIL);
  121.                 /* close TCP connection */
  122.     (* (postclose_t) mail_parameters (NIL,GET_POSTCLOSE,NIL)) (stream->tcpstream);
  123.     if (stream->reply) fs_give ((void **) &stream->reply);
  124.     fs_give ((void **) &stream);/* flush the stream */
  125.   }
  126.   return NIL;
  127. }
  128.  
  129. /* Mail Transfer Protocol deliver mail
  130.  * Accepts: stream
  131.  *        delivery option (MAIL, SEND, SAML, SOML)
  132.  *        message envelope
  133.  *        message body
  134.  * Returns: T on success, NIL on failure
  135.  */
  136.  
  137. long smtp_mail (SMTPSTREAM *stream,char *type,ENVELOPE *env,BODY *body)
  138. {
  139.   char tmp[8*MAILTMPLEN];
  140.   long error = NIL;
  141.   rfc822emit_t f;
  142.   if (!(env->to || env->cc || env->bcc)) {
  143.                   /* no recipients in request */
  144.     smtp_fake (stream,SMTPHARDERROR,"No recipients specified");
  145.     return NIL;
  146.   }
  147.                   /* make sure stream is in good shape */
  148.   smtp_send (stream,"RSET",NIL);
  149.   strcpy (tmp,"FROM:<");    /* compose "MAIL FROM:<return-path>" */
  150.   rfc822_address (tmp,env->return_path);
  151.   strcat (tmp,">");
  152.   if (stream->ok_8bitmime) strcat (tmp," BODY=8BITMIME");
  153.                 /* send "MAIL FROM" command */
  154.   if (!(smtp_send (stream,type,tmp) == SMTPOK)) return NIL;
  155.                 /* negotiate the recipients */
  156.   if (env->to) smtp_rcpt (stream,env->to,&error);
  157.   if (env->cc) smtp_rcpt (stream,env->cc,&error);
  158.   if (env->bcc) smtp_rcpt (stream,env->bcc,&error);
  159.   if (error) {            /* any recipients failed? */
  160.                       /* reset the stream */
  161.     smtp_send (stream,"RSET",NIL);
  162.     smtp_fake (stream,SMTPHARDERROR,"One or more recipients failed");
  163.     return NIL;
  164.   }
  165.                 /* negotiate data command */
  166.   if (!(smtp_send (stream,"DATA",NIL) == SMTPREADY)) return NIL;
  167.                 /* set up error in case failure */
  168.   smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  169.                 /* encode body as necessary */
  170.   if((f = mail_parameters (NIL,GET_RFC822OUTPUT,NIL)) == (rfc822emit_t) rfc822_output){
  171.                 /* encode body as necessary */
  172.       if (stream->ok_8bitmime) rfc822_encode_body_8bit (env,body);
  173.       else rfc822_encode_body_7bit (env,body);
  174.   }
  175.                 /* output data, return success status */
  176.   return (*f) (tmp,env,body,smtp_soutr,stream->tcpstream) &&
  177.     (smtp_send (stream,".",NIL) == SMTPOK);
  178. }
  179.  
  180. /* Mail Transfer Protocol turn on debugging telemetry
  181.  * Accepts: stream
  182.  */
  183.  
  184. void smtp_debug (SMTPSTREAM *stream)
  185. {
  186.   stream->debug = T;        /* turn on protocol telemetry */
  187. }
  188.  
  189.  
  190. /* Mail Transfer Protocol turn off debugging telemetry
  191.  * Accepts: stream
  192.  */
  193.  
  194. void smtp_nodebug (SMTPSTREAM *stream)
  195. {
  196.   stream->debug = NIL;        /* turn off protocol telemetry */
  197. }
  198.  
  199. /* Internal routines */
  200.  
  201.  
  202. /* Simple Mail Transfer Protocol send recipient
  203.  * Accepts: SMTP stream
  204.  *        address list
  205.  *        pointer to error flag
  206.  */
  207.  
  208. void smtp_rcpt (SMTPSTREAM *stream,ADDRESS *adr,long *error)
  209. {
  210.   char tmp[MAILTMPLEN];
  211.   while (adr) {
  212.                 /* clear any former error */
  213.     if (adr->error) fs_give ((void **) &adr->error);
  214.     if (adr->host) {        /* ignore group syntax */
  215.       strcpy (tmp,"TO:<");    /* compose "RCPT TO:<return-path>" */
  216.       rfc822_address (tmp,adr);
  217.       strcat (tmp,">");
  218.                 /* send "RCPT TO" command */
  219.       if (!(smtp_send (stream,"RCPT",tmp) == SMTPOK)) {
  220.     *error = T;        /* note that an error occurred */
  221.     adr->error = cpystr (stream->reply);
  222.       }
  223.     }
  224.     adr = adr->next;        /* do any subsequent recipients */
  225.   }
  226. }
  227.  
  228.  
  229. /* Simple Mail Transfer Protocol send command
  230.  * Accepts: SMTP stream
  231.  *        text
  232.  * Returns: reply code
  233.  */
  234.  
  235. long smtp_send (SMTPSTREAM *stream,char *command,char *args)
  236. {
  237.   char tmp[MAILTMPLEN];
  238.                 /* build the complete command */
  239.   if (args) sprintf (tmp,"%s %s",command,args);
  240.   else strcpy (tmp,command);
  241.   if (stream->debug) mm_dlog (tmp);
  242.   strcat (tmp,"\015\012");
  243.                 /* send the command */
  244.   return (*(postsoutr_t) mail_parameters (NIL,GET_POSTSOUTR,NIL))
  245.                             (stream->tcpstream,tmp)
  246.        ? smtp_reply (stream)
  247.        : smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  248. }
  249.  
  250. /* Simple Mail Transfer Protocol get reply
  251.  * Accepts: SMTP stream
  252.  * Returns: reply code
  253.  */
  254.  
  255. long smtp_reply (SMTPSTREAM *stream)
  256. {
  257.   unsigned long i,j;
  258.   postgetline_t getline =
  259.     (postgetline_t) mail_parameters (NIL,GET_POSTGETLINE,NIL);
  260.   postverbose_t pv;
  261.                 /* flush old reply */
  262.   if (stream->reply) fs_give ((void **) &stream->reply);
  263.                   /* get reply */
  264.   if (!(stream->reply = (*getline) (stream->tcpstream)))
  265.     return smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  266.   if (stream->debug) mm_dlog (stream->reply);
  267.                 /* got an OK reply? */
  268.   if (((i = atoi (stream->reply)) == SMTPOK) && stream->ehlo) {
  269.     char tmp[MAILTMPLEN];    /* yes, make uppercase copy of response text */
  270.     ucase (strcpy (tmp,stream->reply+4));
  271.                 /* command name */
  272.     j = (((long) tmp[0]) << 24) + (((long) tmp[1]) << 16) +
  273.       (((long) tmp[2]) << 8) + tmp[3];
  274.                 /* defined by SMTP 8bit-MIMEtransport */
  275.     if (j == (((long) '8' << 24) + ((long) 'B' << 16) + ('I' << 8) + 'T') &&
  276.     tmp[4] == 'M' && tmp[5] == 'I' && tmp[6] == 'M' && tmp[7] == 'E' &&
  277.     !tmp[8]) stream->ok_8bitmime = T;
  278.                 /* defined by SMTP Size Declaration */
  279.     else if (j == (((long) 'S' << 24) + ((long) 'I' << 16) + ('Z' << 8) + 'E')
  280.          && (!tmp[4] || tmp[4] == ' ')) {
  281.       if (tmp[4]) stream->size = atoi (tmp+5);
  282.       stream->ok_size = T;
  283.     }
  284.                 /* defined by SMTP Service Extensions */
  285.     else if (j == (((long) 'S' << 24) + ((long) 'E' << 16) + ('N' << 8) + 'D')
  286.          && !tmp[4]) stream->ok_send = T;
  287.     else if (j == (((long) 'S' << 24) + ((long) 'O' << 16) + ('M' << 8) + 'L')
  288.          && !tmp[4]) stream->ok_soml = T;
  289.     else if (j == (((long) 'S' << 24) + ((long) 'A' << 16) + ('M' << 8) + 'L')
  290.          && !tmp[4]) stream->ok_saml = T;
  291.     else if (j == (((long) 'E' << 24) + ((long) 'X' << 16) + ('P' << 8) + 'N')
  292.          && !tmp[4]) stream->ok_expn = T;
  293.     else if (j == (((long) 'H' << 24) + ((long) 'E' << 16) + ('L' << 8) + 'P')
  294.          && !tmp[4]) stream->ok_help = T;
  295.     else if (j == (((long) 'T' << 24) + ((long) 'U' << 16) + ('R' << 8) + 'N')
  296.          && !tmp[4]) stream->ok_turn = T;
  297.   }
  298.   else if (i < 100 && (pv = mail_parameters (NIL,GET_POSTVERBOSE,NIL))){
  299.     (*pv) (stream->reply);
  300.     return smtp_reply(stream);
  301.   }
  302.                 /* handle continuation by recursion */
  303.   if (stream->reply[3]=='-') return smtp_reply (stream);
  304.   stream->ehlo = NIL;        /* not doing EHLO any more */
  305.   return i;            /* return the response code */
  306. }
  307.  
  308. /* Simple Mail Transfer Protocol set fake error
  309.  * Accepts: SMTP stream
  310.  *        SMTP error code
  311.  *        error text
  312.  * Returns: error code
  313.  */
  314.  
  315. long smtp_fake (SMTPSTREAM *stream,long code,char *text)
  316. {
  317.                 /* flush any old reply */
  318.   if (stream->reply ) fs_give ((void **) &stream->reply);
  319.                   /* set up pseudo-reply string */
  320.   stream->reply = (char *) fs_get (20+strlen (text));
  321.   sprintf (stream->reply,"%ld %s",code,text);
  322.   return code;            /* return error code */
  323. }
  324.  
  325.  
  326. /* Simple Mail Transfer Protocol filter mail
  327.  * Accepts: stream
  328.  *        string
  329.  * Returns: T on success, NIL on failure
  330.  */
  331.  
  332. long smtp_soutr (void *stream,char *s)
  333. {
  334.   char c,*t;
  335.   postsoutr_t soutr =
  336.     (postsoutr_t) mail_parameters (NIL, GET_POSTSOUTR, 0);
  337.                 /* "." on first line */
  338.   if (s[0] == '.') (*soutr) (stream,".");
  339.                 /* find lines beginning with a "." */
  340.   while (t = strstr (s,"\015\012.")) {
  341.     c = *(t += 3);        /* remember next character after "." */
  342.     *t = '\0';            /* tie off string */
  343.                 /* output prefix */
  344.     if (!(*soutr) (stream,s)) return NIL;
  345.     *t = c;            /* restore delimiter */
  346.     s = t - 1;            /* push pointer up to the "." */
  347.   }
  348.                 /* output remainder of text */
  349.   return *s ? (*soutr) (stream,s) : T;
  350. }
  351.